package org.alis.rpc.route;

import com.google.common.collect.Lists;
import org.alis.rpc.common.protocol.RpcProtocol;
import org.alis.rpc.common.protocol.RpcServiceInfo;
import org.alis.rpc.common.util.ServerUtils;
import org.alis.rpc.handler.RpcClientHandler;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.HashedMap;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * RpcLoadBalance desc: 模版方法构建负载均衡
 *
 * @author luochuan
 * @date 2020/9/30
 */
public abstract class AbstractRpcLoadBalance<V> {

    /**
     * 获取整体服务
     * 解决map值不存在的问题
     * <code>
     *  使用optional 优雅解决
     *  Optional.ofNullable(serviceMap.get(serviceKey)).ifPresentOrElse(list->list.add(rpcProtocol),()->serviceMap.put(serviceKey,Lists.newArrayList(rpcProtocol)));
     * </code>
     * @param connectedServerNodes 链接节点
     * @return 路由
     */
    protected Map<String, List<RpcProtocol>> getServiceMap(Map<RpcProtocol, RpcClientHandler<V>> connectedServerNodes) {
        Map<String, List<RpcProtocol>> serviceMap = new HashedMap<>();
        if (MapUtils.isNotEmpty(connectedServerNodes)) {
            for (RpcProtocol rpcProtocol : connectedServerNodes.keySet()) {
                for (RpcServiceInfo serviceInfo : rpcProtocol.getServiceInfoList()) {
                    String serviceKey = ServerUtils.makeServiceKey(serviceInfo.getServiceName(), serviceInfo.getVersion());
                    // 使用map原生api解决
                    serviceMap.computeIfAbsent(serviceKey,key->Lists.newArrayList()).add(rpcProtocol);
                }
            }
        }
        return serviceMap;
    }

    /**
     * Route the connection for service key
     * @param serviceKey 模版策略key
     * @param connectedServerNodes 链接模版
     * @return 协议
     * @throws Exception 大异常
     */

    public final RpcProtocol route(String serviceKey, Map<RpcProtocol, RpcClientHandler<V>> connectedServerNodes) throws Exception{
        Map<String, List<RpcProtocol>> serviceMap = getServiceMap(connectedServerNodes);
        return Optional.ofNullable(serviceMap.get(serviceKey)).filter(CollectionUtils::isNotEmpty).map(list->doRoute(serviceKey,list)).orElseThrow(()-> new Exception("Can not find connection for service: " + serviceKey));
    }


    /**
     * 开始执行路由
     * @param serviceKey service的key
     * @param addressList 协议列表
     * @return 返回协议
     */
    protected abstract RpcProtocol doRoute(String serviceKey, List<RpcProtocol> addressList);


}
